package by.dvrudenko.avtobaza.dataaccess.impl;

import java.util.List;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.lang3.Validate;
import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dvrudenko.avtobaza.dataaccess.CarDao;
import by.dvrudenko.avtobaza.datamodel.Car;
import by.dvrudenko.avtobaza.datamodel.Car_;

/*
 Для того, чтобы Spring смог сделать инъекцию нашей DAO-имплементации в сервисный класс,
 нам необходимо отметить его аннотацией @Repository. @Repository является наследником
 Spring'овой аннотации @Component, позволяющей ему автоматически зарегестрировать компонент
 в своём контексте для последующей инъекции. Отличие от @Component заключается в семантике 
 аннотации. Отмечая класс как @Repository, мы всегда в будущем сможем узнать, что этот класс 
 мы используем как персистенс-слой.
 */

@Repository
public class CarDaoImpl extends AbstractDaoImpl<Long, Car> implements CarDao {

	public CarDaoImpl() {
		super(Car.class);
	}

	@Override
	public List<Car> getAllCars() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Car> criteria = cBuilder.createQuery(Car.class);
		Root<Car> root = criteria.from(Car.class);

		criteria.select(root);

		TypedQuery<Car> query = getEm().createQuery(criteria);
		List<Car> results = query.getResultList();
		return results;
	}

	@Override
	public Long getCount() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
		Root<Car> root = criteria.from(Car.class);

		criteria.select(cBuilder.count(root));

		TypedQuery<Long> query = getEm().createQuery(criteria);
		return query.getSingleResult();
	}

	@Override
	public List<Car> getAllCars(SingularAttribute<Car, ?> attr,	boolean ascending, int startRecord, int pageSize) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Car> criteria = cBuilder.createQuery(Car.class);
		Root<Car> root = criteria.from(Car.class);

		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attr), ascending));

		TypedQuery<Car> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Car> results = query.getResultList();
		return results;
	}

	@Override // все рабочие машины по грузоподьемности 
	public List<Car> getAllCarsInConditionByField(SingularAttribute<Car, Integer> attr, Integer capacity) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		CriteriaQuery<Car> criteria = cBuilder.createQuery(Car.class);
		Root<Car> root = criteria.from(Car.class);

		criteria.select(root).where(cBuilder.and(cBuilder.equal(root.get(Car_.carCondition), true),
				(cBuilder.greaterThan(root.get(attr),capacity))));

		TypedQuery<Car> query = getEm().createQuery(criteria);
		List<Car> results = query.getResultList();
		return results;
	}
	
	@Override // все рабочие машины по грузородьемности, объему в сотоянии рабочем или на задании
	public List<Car> getAllCarsByWorkConditionVolumeCapacity (Integer volume, Integer capacity, Boolean work, Boolean condition) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Car> criteria = cBuilder.createQuery(Car.class);
		Root<Car> root = criteria.from(Car.class);

		criteria.select(root).where(cBuilder.and(cBuilder.equal(root.get(Car_.work), work),
				(cBuilder.equal(root.get(Car_.carCondition), condition)),
				(cBuilder.greaterThan(root.get(Car_.carryingCapacity),capacity)),
				(cBuilder.greaterThan(root.get(Car_.volumeCapacity),volume))));		
			
		TypedQuery<Car> query = getEm().createQuery(criteria);
		List<Car> results = query.getResultList();
		return results;
	}

	@Override
	public List<Car> getAllCarsInConditionByWork(Boolean work) {
		Validate.notNull(work, "Work attributes can't be null");
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Car> criteria = cBuilder.createQuery(Car.class);
		Root<Car> root = criteria.from(Car.class);
		
		criteria.select(root).where(cBuilder.and(cBuilder.equal(root.get(Car_.work), work),
				(cBuilder.equal(root.get(Car_.carCondition), true))));
		
		TypedQuery<Car> query = getEm().createQuery(criteria);
		List<Car> results = query.getResultList();
		return results;
	}
	

}
